#pragma once
#include "sys/epoll.h"
#include "basic_component/common.hpp"


#define DEFAULT_EVENT_ARR_SIZE 128 //用户层接受已就绪事件的数组大小

class epoll
{
private:
    void create()
    {
        _epfd = ::epoll_create(256/*Linux新内核已将该参数废弃, 传入大于0的size即可*/);
        if(_epfd < 0)
        {
            LOG(wzx::log_level::FATAL) << "epoll create fail !!!";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(wzx::log_level::INFO) << "epoll create success ~~~";
    }

        
    void modify_helper(int op/*ADD, MOD, DEL*/, int sockfd, uint32_t events)
    {
        struct epoll_event ee;
        ee.data.fd = sockfd;
        ee.events = events;
        int ret_epoll_ctl = epoll_ctl(_epfd, op, sockfd, &ee);   
        if(ret_epoll_ctl < 0)
        {
            LOG(wzx::log_level::FATAL) << "epoll_ctl fail !!!";
            exit(EPOLL_CTL_ERR);
        }
    }

public:
    epoll(int event_arr_size = DEFAULT_EVENT_ARR_SIZE, int timeout = -1/*阻塞wait*/)
        :_epfd(DEFAULT_FD)
        ,_event_arr_size(event_arr_size)
        ,_event_arr_ptr(std::make_unique<struct epoll_event[]>(_event_arr_size))
    {
        create();
    }


    
    //EPOLL_CTL_ADD
    void insert(int sockfd, uint32_t events/*事件类型*/)
    {
        modify_helper(EPOLL_CTL_ADD, sockfd, events);
    }

    //EPOLL_CTL_DEL
    void erase(int sockfd)
    {
        modify_helper(EPOLL_CTL_DEL, sockfd, 0/*没用*/);
    }

    //EPOLL_CTL_MOD
    void change(int sockfd, uint32_t events/*事件类型*/)
    {
        modify_helper(EPOLL_CTL_MOD, sockfd, events);
    }


    //这里要用二级指针才能做到输出型参数!!!!!!!!!!
    int wait(struct epoll_event** event_arr/*输出型*/, int timeout = -1/*阻塞wait*/)
    {
        //每次调用epoll_wait(), 内核都会将就绪事件从用户数组的0下标开始拷贝!!! 所以不用判断有效性, [0, ready_fd_count)均为有效事件
        int ready_fd_count = epoll_wait(_epfd, _event_arr_ptr.get()/*返回智能指针的托管对象*/, _event_arr_size, timeout);

        if(ready_fd_count > 0) //有若干个fd的事件就绪了
            LOG(wzx::log_level::INFO) << "several events are ready!!!  ready_fd_count: " << ready_fd_count;

        else if(ready_fd_count == 0) //超时了 -- 没有事件就绪
            LOG(wzx::log_level::INFO) << "time out!!!";

        else //ready_fd_count < 0 -- epoll()出错了
        {
            LOG(wzx::log_level::FATAL) << "epoll wait error !!!";
            exit(EPOLL_WAIT_ERR);
        }

        *event_arr = _event_arr_ptr.get();
        return ready_fd_count;       
    }



private:
    //1. epoll模型的file descriptor
    int _epfd;

    //2. 用户层数组的size
    int _event_arr_size;

    //3. 用户层数组, 用于接受epoll_wait()返回的已就绪就绪事件. 
    std::unique_ptr<struct epoll_event[]> _event_arr_ptr;
};